home *** CD-ROM | disk | FTP | other *** search
/ Aminet 5 / Aminet 5 - March 1995.iso / Aminet / mus / edit / AlgoRhythms.lha / AlgoRhythms / Source / algorhythms.c next >
C/C++ Source or Header  |  1994-12-09  |  59KB  |  1,632 lines

  1. /* AlgoRhythms.c
  2. ** Thomas E. Janzen 4 September 1989; 2-11-90
  3. ** 18 September 1989 26 nov 89 11 December 1989 16 December 1989
  4. ** Music played with this program is
  5. ** Copyright © 1990,1991,1992,1993,1993 Thomas E. Janzen
  6. ** The music is randomized but changes slowly by sinusoidal functions
  7. ** Copyright (c) © 1990, 1991, 1992, 1993 by Thomas E. Janzen
  8. **    All Rights Reserved
  9. **
  10. **    THIS SOFTWARE IS FURNISHED FREE OF CHARGE FOR STUDY AND USE AND MAY
  11. **    BE COPIED ONLY FOR PERSONAL USE OR COMPLETELY AS OFFERED WITH NO
  12. **    CHANGES FOR FREE DISTRIBUTION.  NO TITLE TO AND OWNERSHIP OF THE
  13. **    SOFTWARE IS HEREBY TRANSFERRED.  THOMAS E. JANZEN ASSUMES NO 
  14. **    RESPONSIBILITY FOR THE USE OR RELIABILITY OF THIS SOFTWARE.
  15. **    
  16. **    Thomas E. Janzen
  17. **    208A Olde Derby Road
  18. **    Norwood, MA  02062-1761
  19. **    (617)769-7733
  20. **    tej@world.std.com
  21. **
  22. **  FACILITY:
  23. **
  24. **    AlgoRhythms music improviser on Commodore (TM) Amiga (TM)
  25. **    compiled with SAS/C Amiga Compiler 6.50 
  26. **
  27. **  ABSTRACT:
  28. **
  29. **    AlgoRhythms.c improvises music over MIDI on a COMMODORE Amiga.
  30. **
  31. **  AUTHORS: Thomas E. Janzen
  32. **
  33. **  CREATION DATE:    26-MAR-1990
  34. **
  35. **  MODIFICATION HISTORY:
  36. **    DATE    NAME    DESCRIPTION
  37. 1.01  12 Aug 90 T Janzen Shortened duration string to fit in new gadgets
  38. 1.02   8 Jan 91 Misc. improvements of code; added Events.nv_r_stop_time
  39. 1.03  11 feb 91 misc. and fixed bug of cut off notes on short scales 
  40. 1.04  23 Feb 91 put back autorequesters using new compile command no crash
  41. 1.05  24 Jul 91 created a custom screen; added quartal and quintal scales;
  42. 1.06  24 SEP 91 use algorhythms.col to define colors, update.
  43. 1.07   3 NOV 91 Use struct timeval for all times until it must be double
  44. 1.08  10 NOV 91 TEJ integers in CHARACTER struct.290 notes/sec @ 16V,64 @ 1
  45. 2.0   26 DEC 91 TEJ Record MIDI and save MIDI standard file
  46.                         Use req.library by Colin Fox and Bruce Dawson
  47.                         Use 20 voices.
  48. 2.1   12 JAN 92 Integers for MaxNoteLen and MinNoteLen
  49. 2.2   17 JUL 92 Use AmigaDOS (TM) 2.04 ASL library if available.
  50.             5 SEP 92 Fix texture thickness calc to be 0 to 1 * range
  51.       15 SEP 92 turn off notes in all voices before turning on voices
  52.                 Fix stuck notes in MUSICSERIAL
  53. 2.3   19 SEP 92 Reformat code
  54. 2.4   31 OCT 92 conform to SAS/C 6.0
  55. 2.5   23 FEB 93 interlace option/ 24 FEB 93 add workbench arguments
  56. 2.6   03 APR 93 Integrate internal audio and 8SVX instruments
  57. 2.7   14 OCT 93 used gadtools
  58. 2.8   22 OCT 93 added forms.c, voices.c with gadtools.  Removed gadgets.c
  59.                 removed redundant menus.
  60. 3.0   1  JAN 94 added colors events.  General 3.0 Version additions.
  61.       26 NOV 94 fixed file drawer bug; released as Version 3.0
  62.       5 Dec  94 fix form path bug
  63. 3.1   10 December 94.  Made it run even if serial device fails.
  64.                        Improved serial device error reporting.
  65. **
  66. */
  67.  
  68. #include <stdlib.h>
  69. #include <stdio.h>
  70. #include <math.h>
  71. #include <string.h>
  72. #include <limits.h>
  73. #include <intuition/intuition.h>
  74. #include <exec/memory.h>
  75. #include <exec/interrupts.h>
  76. #include <exec/devices.h>
  77. #include <devices/serial.h>
  78. #include <devices/timer.h>
  79. #include <proto/all.h>
  80. #include <Workbench/startup.h>
  81. #include <Libraries/asl.h> /* asl */
  82. #include <proto/asl.h>     /* asl */
  83. #include <utility/tagitem.h>
  84.  
  85. #include <proto/req.h>
  86. #include "AlgoRhythms.h"
  87. #include "MusicTimer.h"
  88. #include "MusicSerial.h"
  89. #include "Files.h"
  90. #include "Scales.h"
  91. #include "audio.h"
  92. #ifndef CLI
  93. #include "Window.h"
  94. #include "DrawForm.h"
  95. #include "Menus.h"
  96. #include "Record.h"
  97. #include "Forms.h"
  98. #include "Voices.h"
  99. #include "musicrexx.h"
  100. #include "colors.h"
  101. #endif
  102.  
  103. #define TWOPI   (2 * PI)
  104. #define PROJECT (0)
  105. #define FORM    (1)
  106. #define SCALE   (2)
  107. #define CHANNEL (3)
  108.  
  109. /* 
  110. ** This type of structure holds the instantaneous values for the range and
  111. ** median pitch, dynamic, duration, and thickness. 
  112. */
  113. typedef struct character_struct CHARACTER_TYPE;
  114. struct character_struct 
  115. {
  116.     int ch_i_pitch_range,   /* range of pitches                 */
  117.         ch_i_pitch_mean,    /* ~ median pitch                   */
  118.         ch_i_dynamic_range, /* range of dynamic levels          */
  119.         ch_i_dynamic_mean,  /* ~ median of dynamic levels       */
  120.         ch_i_dur_range,     /* range of durations               */
  121.         ch_i_dur_mean,      /* ~ median of durations            */
  122.         ch_i_thickness;     /* number of voices playing at once */
  123. };
  124.  
  125. /* 
  126. ** Inter-modular Globals: gi_fubar, scale, and gi_quit 
  127. ** If the serial channel fails, gi_fubar is set and the program exits. 
  128. */
  129. int gi_fubar    = FALSE,
  130.     gi_quit     = FALSE;     /* Was told to quit out of the program     */
  131. static int  playing     = FALSE,    /* playing music                    */
  132.             started     = FALSE,    /* Has started playing music        */
  133.             done        = FALSE,    /* done playing because it timed out*/
  134.             delay_ticks =  8,       /* fiftieths of a second to wait    */
  135.             range       = 10,       /* length of scale array            */
  136.             half_range  =  5,       /* half of range (scale array len)  */
  137.             num_voices  =  4,       /*                                  */
  138.             scale[120]  = {48,50,53,55,58,60,62,65,67,70,72,74,77};
  139. static struct timeval range_time = {0, 0};
  140.  
  141. static double cvt_time_secs(struct timeval *Time);
  142. static void stop_note(  NOTE_EVENT_TYPE *new_event, 
  143.                         struct timeval *music_time);
  144. static void ranges( const FORM_TYPE *form, const struct timeval *cur_time, 
  145.                     CHARACTER_TYPE *next_now, NOTE_LEN_TYPE *note_len);
  146. static void make_event(  NOTE_EVENT_TYPE *new_event, CHARACTER_TYPE *style,
  147.                         struct timeval *music_time, const int event_index,
  148.                         NOTE_LEN_TYPE *note_len);
  149. static NOTE_EVENT_TYPE reset_event 
  150.         = {{0, 0}, {0, 0}, {0, 0}, 0, 0, -1, 0, 0, 0, 0, 0};
  151. const struct timeval zero_time = {0, 0};
  152. static struct timeval stop_time;  /* The Time that the music stopped */
  153.  
  154. #ifndef CLI
  155. static void parse_menu(  const int class, const int code, 
  156.                         struct timeval *str_time, FORM_TYPE *form,
  157.                         struct timeval *duration, int *tempo, 
  158.                         NOTE_EVENT_TYPE *events, NOTE_LEN_TYPE *note_len);
  159. #endif
  160. int main(int argc, char *argv[]) /* CLI version */
  161. /*
  162. ** FUNCTIONAL DESCRIPTION:
  163. **  AlgoRhythms main routine including Intuition maintenance loop
  164. ** 
  165. ** ARGUMENTS: (only for CLI version)
  166. **
  167. **  argc-
  168. **         description: count of arguments; only 2 allowed 
  169. **           data_type: int
  170. **              access: read only
  171. **
  172. **  argv-
  173. **         description: name of form file for CLI version only
  174. **           data_type: pointer to pointers to char
  175. **              access: read only
  176. **
  177. ** DESIGN:
  178. **  ROUTINE main
  179. **  : open_wind()
  180. **  : open_midi_port()
  181. **  : IF midi port cannot be opened
  182. **  : : GOTO cleanup2
  183. **  : ENDIF
  184. **  : init_menu()
  185. **  : init temp, range, scale, and half_range.
  186. **  : get process pointer for ASL library
  187. **  : get process window pointer for ASL library
  188. **  : init voices
  189. **  : #if CLI
  190. **  : IF argument count was wrong
  191. **  : : put usage message 
  192. **  : : GOTO cleanup2
  193. **  : ENDIF
  194. **  : copy argv[1] to file_string
  195. **  : read the specified form file
  196. **  : #else
  197. **  : randomize pitch, rhythm, dynamic, texture
  198. **  : IF workbench passed arguments
  199. **  : : IF arguments >= 1
  200. **  : : : get argument
  201. **  : : : IF argument is locked
  202. **  : : : : save current directory
  203. **  : : : : read form file from workbench argument
  204. **  : : : : restore directory
  205. **  : : : ENDIF
  206. **  : : ELSE
  207. **  : : : just read the form file from an unlocked workbench argument
  208. **  : : ENDIF
  209. **  : : set the window title from the workbench argument
  210. **  : ENDIF
  211. **  : #endif
  212. **  : IF file read in OK
  213. **  : : IF tempo = 0
  214. **  : : : init delay_ticks to 0
  215. **  : : ELSE
  216. **  : : : delay_ticks = 50/ tempo
  217. **  : : ENDIF
  218. **  : : half_range = range / 2
  219. **  : ENDIF
  220. **  : #endif
  221. **  : start_time()
  222. **  : put system time into sys_time
  223. **  : set pseudo-random number generator with time microseconds
  224. **  : start_time = sys_time
  225. **  : clear music_time
  226. **  : #ifndef CLI
  227. **  : draw_form()
  228. **  : #endif
  229. **  : cur_style = ranges
  230. **  : #if CLI
  231. **  : init started = TRUE, done = FALSE send_function(STARTFUNCT)
  232. **  : #endif
  233. **  : WHILE (not quitting)
  234. **  : : music_time = current time - start_time
  235. **  : : WHILE (playing = (!done AND started) AND !gi_quit)
  236. **  : : : music_time = current_time - start_time
  237. **  : : : IF it is time to update the instantaneous parameter ranges
  238. **  : : : : ranges(form, music_time, cur_style)
  239. **  : : : : range_time = music_time + 1/10 second
  240. **  : : : ENDIF
  241. **  : : : FOR event_index = 0 to num_voices
  242. **  : : : : IF music_time > the voice's stop_time 
  243. **  : : : : : stop the note for that voice
  244. **  : : : : ENDIF
  245. **  : : : ENDFOR
  246. **  : : : FOR event_index = 0 to num_voices
  247. **  : : : : IF events[event_index] is not playing
  248. **  : : : : : make_event(events[event_index], cur_style, music_time)
  249. **  : : : : ENDIF
  250. **  : : : ENDFOR
  251. **  : : : #if not CLI
  252. **  : : : draw_time() (move time cursor on screen)
  253. **  : : : WHILE (message = GetMsg(w->UserPort) service Intuition windowing
  254. **  : : : : class = message->Class; code = message->Code
  255. **  : : : : ReplyMsg()
  256. **  : : : : IF class = MENUPICK AND code = MENUNULL
  257. **  : : : : : go to top of WHILE
  258. **  : : : : ENDIF
  259. **  : : : : parse_menu(class, code, start_time, form, piece_duration, 
  260. **                  tempo, events)
  261. **  : : : ENDWHILE
  262. **  : : : IF delay_ticks != 0
  263. **  : : : : FOR tick = 0 to delays_ticks BY 2
  264. **  : : : : : send MIDI clock byte
  265. **  : : : : : Delay about 1/25 sec
  266. **  : : : : ENDFOR
  267. **  : : : ENDIF
  268. **  : : : IF done = (music_time > piece_duration)
  269. **  : : : : #if CLI
  270. **  : : : : gi_quit = done
  271. **  : : : : #endif
  272. **  : : : : stop_all_notes()
  273. **  : : : : started = FALSE
  274. **  : : : ENDIF
  275. **  : : ENDWHILE
  276. **  : : #if not CLI
  277. **  : : set up Intuition signals
  278. **  : : wait for Intuition windowing signal
  279. **  : : interpret Intuition signal
  280. **  : : #endif
  281. **  : : continue
  282. **  : cleanup1:
  283. **  : #if not CLI
  284. **  : close_menu()
  285. **  : #endif
  286. **  : stop_all_notes()
  287. **  : Delay a little
  288. **  : remove_timer()
  289. **  : cleanup2:
  290. **  : #if not CLI
  291. **  : shut_window()
  292. **  : erase_recording()
  293. **  : #endif
  294. **  : stop_midi()
  295. **  : exit
  296. ** ENDROUTINE
  297. */
  298. {
  299.     static FORM_TYPE form = {{180.0, -PID2, 200.0, -PID2},
  300.                              {200.0, PID2,  180.0,  PID2},
  301.                              {170.0, -PID2, 165,   -PID2},
  302.                              {0.0,   0.0,   190.0, -PID2}};
  303.     auto char file_string[128];
  304.     auto int sts,
  305.              vox_index;
  306. #ifndef CLI
  307.     static int  code, 
  308.                 class,              /* mouse codes esp. for menus */
  309.                 main_mask,
  310.                 signal;
  311.     auto struct IntuiMessage *message;
  312.     static struct Process *process;
  313.     static APTR old_error_window;
  314.     auto struct WBStartup *wb_msg;
  315.     auto struct WBArg *wb_arg;
  316.     auto BPTR olddir;
  317. #endif
  318.     static int event_index =  0;   /* Index into Events                */
  319.     register int tick = 0;    /* Index for counting 1/25's of a second*/
  320.     auto int    tempo,        /* ticks per second */
  321.                 voice_index;  /* index           */
  322.     auto struct timeval   tenth_second = {0, 1000000};
  323.     auto struct timeval music_time,
  324.                         start_time, /* the current time in seconds */
  325.                         piece_duration = {600, 0}, /* piece length */
  326.                         sys_time;
  327.     static CHARACTER_TYPE   cur_style;
  328.     static NOTE_LEN_TYPE note_len = {100, 2000, 2000};
  329.     static NOTE_EVENT_TYPE events[MAXVOICE];  /* currently playing notes */
  330.  
  331. #ifndef CLI
  332.     open_wind();   /* Open the window for AlgoRhythms */
  333. #endif
  334.  
  335.     open_midi_port();    /* Open the serial port for MIDI use */
  336.  
  337.     if (gi_fubar) 
  338.     {    
  339.         /* 
  340.         ** Get out if serial port failed to open 
  341.         */
  342.         goto cleanup2;    /* skip out on failure */
  343.     }
  344. #ifndef CLI
  345.     init_menu();                     /* Set up the menu              */
  346.     tempo = 50 / delay_ticks;        /* Tempo is pulses per second   */
  347.     range = install_scale(1, scale); /* Install a musical scale      */
  348.     half_range = range / 2;          /* recalculate half_range       */
  349.     process = (struct Process *)FindTask(NULL);
  350.     old_error_window = process->pr_WindowPtr;
  351.     process->pr_WindowPtr = (APTR)w;
  352.  
  353.     open_orch_window();
  354.     open_forms_window();
  355.     open_voices_window();
  356.     open_colors_window();
  357.     init_rexx();
  358. #endif
  359.     for (voice_index = 0; voice_index < MAXVOICE; voice_index++)
  360.     {
  361.         events[voice_index].nv_i_id = voice_index;
  362.         events[voice_index].nv_i_scale_index      = half_range;
  363.         events[voice_index].nv_i_dynamic    = 0.0;
  364.         events[voice_index].nv_r_start_time.tv_micro 
  365.             = events[voice_index].nv_r_start_time.tv_secs 
  366.             = events[voice_index].nv_r_stop_time.tv_micro  
  367.             = events[voice_index].nv_r_stop_time.tv_secs 
  368.             = events[voice_index].nv_r_duration.tv_micro  
  369.             = events[voice_index].nv_r_duration.tv_secs 
  370.             = events[voice_index].nv_i_channel
  371.             =   0;
  372.         events[voice_index].nv_i_cur_pitch    =  60;
  373.         events[voice_index].nv_i_low_pitch    =  24;
  374.         events[voice_index].nv_i_high_pitch   = 108;
  375.         events[voice_index].nv_i_walking
  376.             = events[voice_index].nv_i_audio
  377.             = events[voice_index].nv_i_was_audio
  378.             = FALSE;
  379.         events[voice_index].nv_i_playing = FALSE;
  380.         events[voice_index].nv_i_audio_chan = C_NO_CHAN;
  381.     }
  382.     for (voice_index = 0; voice_index < 4; voice_index++)
  383.     {
  384.         events[voice_index].nv_i_walking
  385.             = events[voice_index].nv_i_audio
  386.             = events[voice_index].nv_i_was_audio
  387.             = TRUE;
  388.     }
  389.     init_audio();
  390.     /*
  391.     ** set the form of the piece
  392.     */
  393. #ifdef CLI
  394.     if (argc != 2) 
  395.     {
  396.         puts("usage: AlgoRhythmsCLI {Form_file}\n");
  397.         goto cleanup2;
  398.     }
  399.     strcpy(file_string, argv[1]);
  400.     sts = read_file(file_string, &piece_duration, &range, scale, 
  401.                     &num_voices, &tempo, &form, events, 
  402.                     ¬e_len);
  403. #else
  404.  
  405.     if (0 == argc) /* if workbench passed an argument */
  406.     {
  407.         wb_msg = (struct WBStartup *)argv;
  408.         if (wb_msg->sm_NumArgs >= 2)
  409.         {
  410.             wb_arg = wb_msg->sm_ArgList;
  411.             wb_arg++;
  412.             if (wb_arg->wa_Lock != NULL)
  413.             {
  414.                 olddir = CurrentDir(wb_arg->wa_Lock);
  415.                 strcpy(file_string, wb_arg->wa_Name);
  416.                 sts = read_file(file_string, &piece_duration, &range, 
  417.                                 scale, &num_voices, &tempo, &form, events, 
  418.                                 ¬e_len);
  419.                 CurrentDir(olddir);
  420.             }
  421.             else
  422.             {
  423.                 strcpy(file_string, wb_arg->wa_Name);
  424.                 sts = read_file(file_string, &piece_duration, &range, 
  425.                                 scale, &num_voices, &tempo, &form, events, 
  426.                                 ¬e_len);
  427.             }
  428.             SetWindowTitles(w, file_string, (void *) -1L);
  429.  
  430.         }
  431.     }
  432. #endif
  433.     if (!sts) 
  434.     {
  435.         if (0 == tempo) 
  436.         {
  437.             delay_ticks = 0;
  438.         }
  439.         else 
  440.         {
  441.             delay_ticks = 50 / tempo;
  442.         }
  443.         half_range = range / 2;
  444.     }
  445.     start_timer();              /* Start timer device                   */
  446.     GetSysTime(&sys_time);      /* Get the time                         */
  447.     srand(sys_time.tv_micro);   /* use microseconds time to seed random */
  448.     /* 
  449.     ** Save Time the program started 
  450.     */
  451.     start_time = sys_time;
  452.     music_time.tv_micro = music_time.tv_secs = 0; /*Clear time to start*/
  453.  
  454. #ifndef CLI
  455.     /* 
  456.     ** Initialize the Form parameters as random 
  457.     */
  458.     randomize_parameter(&form.frm_s_pitch);
  459.     randomize_parameter(&form.frm_s_rhythm);
  460.     randomize_parameter(&form.frm_s_dynamic);
  461.     randomize_parameter(&form.frm_s_texture);
  462.     draw_form(&piece_duration, &form);
  463.     set_form_gadgets(&form, &piece_duration, &tempo, events, ¬e_len, 
  464.         num_voices);
  465.     set_voice_gadgets(events);
  466. #endif
  467.     /* 
  468.     ** Initialize the ranges(pitch, dynamic, duration) 
  469.     */
  470.     ranges(&form, &music_time, &cur_style, ¬e_len);
  471. #ifdef CLI
  472.     started = TRUE;
  473.     done = FALSE;
  474.     send_function(STARTFUNCT);
  475. #endif
  476.     /* 
  477.     ** Master Control Loop 
  478.     ** Program stays in here until it gi_quits 
  479.     */
  480.     while ( !(gi_quit)) /* until we quit */
  481.     {
  482.         GetSysTime(&sys_time);
  483.         music_time = sys_time;
  484.         SubTime(&music_time, &start_time); /*music_time=cur_style - Start*/
  485.         /* 
  486.         ** as long as you are playing 
  487.         */
  488.         while (playing = ( !done && started) && !gi_quit) 
  489.         {
  490.             /* 
  491.             **  get the time and convert
  492.             ** to double normalize to
  493.             ** the starting time of the
  494.             ** piece of music
  495.             */
  496.             GetSysTime(&sys_time);    
  497.             music_time = sys_time;
  498.             SubTime(&music_time, &start_time);
  499.             /* 
  500.             ** Get the instantaneous mean & range of
  501.             ** pitch, dynamics, duration, and texture
  502.             */
  503.             if (-1 == CmpTime(&music_time, &range_time))
  504.             {
  505.                 ranges(&form, &music_time, &cur_style, ¬e_len);
  506.                 range_time = music_time;
  507.                 AddTime(&range_time, &tenth_second);
  508.             }
  509.             /* 
  510.             ** scan the voices
  511.             */
  512.             for (event_index = 0; event_index < MAXVOICE; event_index++)
  513.             {    
  514.                 /* if the note event is complete */ 
  515.                 if (-1 == CmpTime(&music_time,
  516.                     &events[event_index].nv_r_stop_time))
  517.                 {
  518.                     stop_note(&events[event_index], &music_time);
  519.                 }
  520.             }
  521.             for (event_index = 0; event_index < num_voices; event_index++)
  522.             {    
  523.                 if (!(events[event_index].nv_i_playing))
  524.                 {
  525.                     make_event(&events[event_index], &cur_style, 
  526.                                 &music_time, event_index, ¬e_len);
  527.                 }
  528.             }
  529.             /* 
  530.             ** Indicate the time on the form graph 
  531.             */
  532. #ifndef CLI
  533.             draw_time(&music_time, &piece_duration);
  534.             /* If there is an action, then find out what */
  535.             while (message = (struct IntuiMessage *) GetMsg(w->UserPort))
  536.             {
  537.                 class = message->Class;
  538.                 code = message->Code;
  539.                 ReplyMsg((struct Message *)message);
  540.                 if ((MENUPICK == class) && (MENUNULL == code))
  541.                 {
  542.                     continue;
  543.                 }
  544.                 parse_menu(class, code, &start_time, &form, 
  545.                           &piece_duration, &tempo, events, ¬e_len);
  546.             }
  547.             if (process_form_events(&form, &piece_duration,
  548.                 &tempo, events, ¬e_len, &num_voices, scale, &range,
  549.                 &half_range, &delay_ticks))
  550.             {
  551.                 set_form_gadgets(&form, &piece_duration, &tempo, events, 
  552.                     ¬e_len, num_voices);
  553.             }
  554.             process_color_events();
  555.             if (process_voice_events(events))
  556.             {
  557.                 set_voice_gadgets(events);
  558.             }
  559.             process_audio_events();
  560.             switch (process_rexx(&form, &piece_duration, &tempo, 
  561.                          events, ¬e_len, &num_voices, scale, &range,
  562.                          &half_range, &delay_ticks))
  563.             {
  564.                 case C_REXX_CHANG_VOICE:
  565.                     set_voice_gadgets(events);
  566.                     break;
  567.                 case C_REXX_CHANG_FORM:
  568.                     set_form_gadgets(&form, &piece_duration, &tempo, 
  569.                     events, ¬e_len, num_voices);
  570.                     break;
  571.                 case C_REXX_CHANG_BOTH:
  572.                     set_voice_gadgets(events);
  573.                     set_form_gadgets(&form, &piece_duration, &tempo, 
  574.                     events, ¬e_len, num_voices);
  575.                     break;
  576.                 case C_REXX_PLAY:
  577.                     started = TRUE;
  578.                     done = FALSE;
  579.                     GetSysTime(&start_time);
  580.                     play_note_on(&reset_event);
  581.                     for (vox_index = 0; vox_index < MAXVOICE; vox_index++)
  582.                     {
  583.                         events[vox_index].nv_r_start_time
  584.                             = events[vox_index].nv_r_duration
  585.                             = events[vox_index].nv_r_stop_time
  586.                             = zero_time;
  587.                     }
  588.                     send_function(STARTFUNCT);
  589.                     range_time = zero_time;
  590.                     break;
  591.                 case C_REXX_STOP:
  592.                     started = FALSE;
  593.                     for (vox_index = 0; 
  594.                     vox_index < MAXVOICE; vox_index++)
  595.                     {
  596.                         events[vox_index].nv_r_start_time 
  597.                             = events[vox_index].nv_r_duration 
  598.                             = events[vox_index].nv_r_stop_time 
  599.                             = zero_time;
  600.                     }
  601.                     stop_all_notes(events);
  602.                     GetSysTime(&stop_time);
  603.                     break;
  604.                 case C_REXX_CONTINUE:
  605.                     started = TRUE;
  606.                     play_note_on(&reset_event);
  607.                     GetSysTime(&sys_time);
  608.                     start_time.tv_secs 
  609.                         += (sys_time.tv_secs - stop_time.tv_secs);
  610.                     break;
  611.             }
  612. #endif
  613.             /* 
  614.             ** If delay_ticks is not zero then pause to
  615.             ** simulate a regular beat 
  616.             */
  617.             if (delay_ticks != 0) 
  618.             {
  619.                 for (tick = 0; tick < delay_ticks; tick += 2)
  620.                 {
  621.                     send_function(CLOCKFUNCT); /* Send MIDI timing clock */
  622.                     Delay(1); /* wait 1/50's of a sec*/
  623.                 }
  624.             }
  625.             /* 
  626.             ** if the piece is done, 
  627.             */
  628.             if (done = (-1 == CmpTime(&music_time, &piece_duration)))
  629.             {
  630. #ifdef CLI
  631.                 gi_quit = done;
  632. #endif
  633.                 stop_all_notes(events);
  634.                 started = FALSE;
  635.             }
  636.         }
  637.         /*
  638.         ** if the piece is playing, then wait, free up AmigaDOS,
  639.         ** until a mouse event hits this window
  640.         */
  641. #ifndef CLI
  642.         main_mask = 1L << w->UserPort->mp_SigBit;
  643.         signal 
  644.             = Wait(main_mask | forms_mask | orch_mask 
  645.                 | voices_mask | rexx_mask | colors_mask);
  646.         /*
  647.         ** When the event hits, find out what 
  648.         */
  649.         if (signal & main_mask)
  650.         {
  651.             while (message = (struct IntuiMessage *) GetMsg(w->UserPort))
  652.             {
  653.                 class = message->Class;
  654.                 code = message->Code;
  655.                 ReplyMsg((struct Message *)message);
  656.                 if ((MENUPICK == class) && (MENUNULL == code))
  657.                 {
  658.                     continue;
  659.                 }
  660.                 parse_menu(class, code, &start_time, &form, &piece_duration,
  661.                         &tempo, events, ¬e_len);
  662.             }
  663.         }
  664.         if (signal & forms_mask)
  665.         {
  666.             if (process_form_events(&form, &piece_duration,
  667.                 &tempo, events, ¬e_len, &num_voices, scale, &range,
  668.                 &half_range, &delay_ticks))
  669.             {
  670.                 set_form_gadgets(&form, &piece_duration, &tempo, events, 
  671.                     ¬e_len, num_voices);
  672.             }
  673.         }
  674.         if (signal & voices_mask)
  675.         {
  676.             if (process_voice_events(events))
  677.             {
  678.                 set_voice_gadgets(events);
  679.             }
  680.         }
  681.         if (signal & orch_mask)
  682.         {
  683.             process_audio_events();
  684.         }
  685.         if (signal & colors_mask)
  686.         {
  687.             process_color_events();
  688.         }
  689.         if (signal & rexx_mask)
  690.         {
  691.             switch (process_rexx(&form, &piece_duration, &tempo, 
  692.                          events, ¬e_len, &num_voices, scale, &range,
  693.                          &half_range, &delay_ticks))
  694.             {
  695.                 case C_REXX_CHANG_VOICE:
  696.                     set_voice_gadgets(events);
  697.                     break;
  698.                 case C_REXX_CHANG_FORM:
  699.                     set_form_gadgets(&form, &piece_duration, &tempo, 
  700.                     events, ¬e_len, num_voices);
  701.                     break;
  702.                 case C_REXX_CHANG_BOTH:
  703.                     set_voice_gadgets(events);
  704.                     set_form_gadgets(&form, &piece_duration, &tempo, 
  705.                     events, ¬e_len, num_voices);
  706.                     break;
  707.                 case C_REXX_PLAY:
  708.                     started = TRUE;
  709.                     done = FALSE;
  710.                     GetSysTime(&start_time);
  711.                     play_note_on(&reset_event);
  712.                     for (vox_index = 0; vox_index < MAXVOICE; vox_index++)
  713.                     {
  714.                         events[vox_index].nv_r_start_time
  715.                             = events[vox_index].nv_r_duration
  716.                             = events[vox_index].nv_r_stop_time
  717.                             = zero_time;
  718.                     }
  719.                     send_function(STARTFUNCT);
  720.                     range_time = zero_time;
  721.                     break;
  722.                 case C_REXX_STOP:
  723.                     started = FALSE;
  724.                     for (vox_index = 0; 
  725.                     vox_index < MAXVOICE; vox_index++)
  726.                     {
  727.                         events[vox_index].nv_r_start_time 
  728.                             = events[vox_index].nv_r_duration 
  729.                             = events[vox_index].nv_r_stop_time 
  730.                             = zero_time;
  731.                     }
  732.                     stop_all_notes(events);
  733.                     GetSysTime(&stop_time);
  734.                     break;
  735.                 case C_REXX_CONTINUE:
  736.                     started = TRUE;
  737.                     play_note_on(&reset_event);
  738.                     GetSysTime(&sys_time);
  739.                     start_time.tv_secs 
  740.                         += (sys_time.tv_secs - stop_time.tv_secs);
  741.                     break;
  742.             }
  743.         }
  744. #endif
  745.         continue; /* continue in Master Control Loop until gi_quit */
  746.     }    
  747. cleanup1:        /* normal dump out of the program */
  748. #ifndef CLI
  749.     process->pr_WindowPtr = old_error_window;
  750.  
  751.     close_colors();
  752.     close_voices();
  753.     close_forms();
  754.     close_orch();
  755.     shut_rexx();
  756. #endif
  757.     stop_all_notes(events);    /* turn off all the notes */
  758.     de_init_audio();
  759.     Delay(10);         /* wait 1/5 seconds */
  760.     remove_timer(); /* release the timer device */
  761. cleanup2:             /* exit here if the serial port wouldn't open*/
  762. #ifndef CLI
  763.     close_menu();        /* shut down menu */
  764.     shut_window();  /* remove the window */
  765.     erase_recording();
  766. #endif
  767.     stop_midi();    /* close the serial device */
  768.  
  769.     exit(EXIT_SUCCESS);        /* exit program */
  770. }
  771.  
  772. static void ranges( const FORM_TYPE *form, 
  773.                     const struct timeval *cur_time,
  774.                     CHARACTER_TYPE *next_now, NOTE_LEN_TYPE *note_len)
  775. /*
  776. ** FUNCTIONAL DESCRIPTION:
  777. **  Calculates the current ranges for pitch, dynamic, duration, and the
  778. **  number of voices playing.
  779. **
  780. ** ARGUMENTS:
  781. **
  782. **  form-
  783. **         description: period and phase of mean and range 
  784. **                      for pitch,dyn,text,rhythm 
  785. **           data_type: pointer to FORM_TYPE
  786. **              access: read only
  787. **
  788. **  cur_time-
  789. **         description: period and phase of mean and range 
  790. **           data_type: pointer to timeval
  791. **              access: read only
  792. **
  793. **  next_now-
  794. **         description: period and phase of mean and range 
  795. **           data_type: pointer to CHARACTER_TYPE
  796. **              access: write only
  797. **
  798. ** DESIGN:
  799. **  ROUTINE ranges
  800. **  : phase = TWOPI * (CurrentTime->tv_secs 
  801. **         + (CurrentTime->tv_micro * 1E-6))
  802. **  : next_now->ch_i_pitch_range = (sin(phase / pitch->prm_d_range_cycle 
  803. **         + pitch->prm_d_range_phase) + 1) * realhalf_range
  804. **  : next_now->ch_i_pitch_mean = (sin(phase / pitch->prm_d_mean_cycle
  805. **         + pitch->prm_d_mean_phase) + 1.0) * realhalf_range 
  806. **  : IF pitch_mean and range could create negative note numbers
  807. **  : : center up range smaller
  808. **  : ENDIF
  809. **  : ELSE
  810. **  : : IF pitch_mean and range could create scale notes beyond the scale
  811. **  : : : center up range smaller
  812. **  : : ENDIF
  813. **  : ENDIF
  814. **  : next_now->ch_i_dynamic_range= (sin(phase / dynamic->prm_d_range_cycle
  815. **         + dynamic->prm_d_range_phase) + 1.01) * 63.0
  816. **  : next_now->ch_i_dynamic_mean = (sin(phase / dynamic->prm_d_mean_cycle 
  817. **         + dynamic->prm_d_mean_phase) + 1.01) * 50.0 + 25.0
  818. **  : next_now->ch_i_dur_range = (sin(phase / duration->prm_d_range_cycle 
  819. **         + duration->prm_d_range_phase) + 1.01) / 2.0 * dif_note_len_ms
  820. **  : next_now->ch_i_dur_mean = (sin(phase / duration->prm_d_mean_cycle 
  821. **         + duration->prm_d_mean_phase) + 1.01) / 2.0 * dif_note_len_ms 
  822. **         + min_note_len_ms
  823. **  : next_now->ch_i_thickness = (sin(phase / thickness->prm_d_range_cycle
  824. **         + thickness->prm_d_range_phase) + 1) / 2 
  825. **         * (num_voices - 1)
  826. **  ENDROUTINE
  827. */
  828. {
  829.     auto double realrange,      /* floating-point version of range      */
  830.                 realhalf_range, /* floating-point version of half_range */
  831.                 phase;
  832.                 
  833.     realrange = (double) range;
  834.     realhalf_range = (double) half_range;
  835.     
  836.     phase = TWOPI * ((double)(cur_time->tv_secs) +
  837.                 ((double)(cur_time->tv_micro) * 1E-6));
  838.     next_now->ch_i_pitch_range 
  839.         = (int)floor(((sin((phase / form->frm_s_pitch.prm_d_range_cycle) 
  840.         + form->frm_s_pitch.prm_d_range_phase) + 1.0) * realhalf_range));
  841.     /* pitch range is a sin function of time*/
  842.     next_now->ch_i_pitch_mean 
  843.         = (int)floor(((sin((phase / form->frm_s_pitch.prm_d_mean_cycle)
  844.         + form->frm_s_pitch.prm_d_mean_phase) + 1.0) * realhalf_range)); 
  845.     /* 
  846.     ** The following bounds checking prevents pitch range from
  847.     ** overlapping into non-usable notes (less than 0 and higher
  848.     ** than the top scale note).  It forces the range to be
  849.     ** from top or bottom of the scale through the mean and
  850.     ** an equal distance to the other side
  851.     */
  852.     if ((next_now->ch_i_pitch_mean - (next_now->ch_i_pitch_range / 2)) < 0)
  853.     {
  854.         next_now->ch_i_pitch_mean = 1 + ((next_now->ch_i_pitch_mean
  855.             + (next_now->ch_i_pitch_range / 2)) / 2);
  856.     }
  857.     else
  858.     {
  859.         if ((next_now->ch_i_pitch_mean + (next_now->ch_i_pitch_range / 2)) 
  860.             > (double)range) 
  861.         {
  862.             next_now->ch_i_pitch_mean = realhalf_range - 1
  863.                 + ((next_now->ch_i_pitch_mean 
  864.                 - (next_now->ch_i_pitch_range / 2)) / 2);
  865.         }
  866.     }
  867.     next_now->ch_i_dynamic_range  
  868.         = (int)floor(((
  869.         sin((phase / (form->frm_s_dynamic.prm_d_range_cycle))
  870.         + form->frm_s_dynamic.prm_d_range_phase) + 1.01) * 63.0));
  871.     /* Range of dynamics is a sin function of time */
  872.     next_now->ch_i_dynamic_mean
  873.         = (int)floor(((sin(phase / form->frm_s_dynamic.prm_d_mean_cycle 
  874.         + form->frm_s_dynamic.prm_d_mean_phase) + 1.01) * 50.0 + 25.0));
  875.     /* Median dynamic is a sin function of time */
  876.     next_now->ch_i_dur_range 
  877.         = (int)floor((((sin(phase / form->frm_s_rhythm.prm_d_range_cycle 
  878.         + form->frm_s_rhythm.prm_d_range_phase) + 1.01) / 2.0)
  879.         * (double)note_len->len_i_dif));
  880.     /* 
  881.     ** Range of durations is a sin function of time
  882.     */
  883.     next_now->ch_i_dur_mean 
  884.         = (int)floor(((sin(phase / form->frm_s_rhythm.prm_d_mean_cycle 
  885.         + form->frm_s_rhythm.prm_d_mean_phase) + 1.01) / 2.0 
  886.         * (double)note_len->len_i_dif)) + note_len->len_i_min; 
  887.     /* Median duration is a sin function of time */
  888.     next_now->ch_i_thickness   
  889.         = (int)floor((((sin((phase / form->frm_s_texture.prm_d_range_cycle)
  890.         + form->frm_s_texture.prm_d_range_phase) + 1.0) / 2.0 
  891.         * (double)(num_voices - 1))) + 0.5);
  892.     /* the number of voices playing is a sin function of time */
  893.     return;
  894. }
  895.  
  896. void randomize_parameter(PARAMETER_TYPE *param)
  897. /*
  898. ** FUNCTIONAL DESCRIPTION:
  899. **  Calculate random values for period/phase of mean/range for parameter
  900. **
  901. ** RETURN VALUE:
  902. **      description: randomized period/phase of mean/range
  903. **        data_type: PARAMETER_TYPE
  904. **
  905. ** DESIGN:
  906. **  ROUTINE randomize_parameter()
  907. **  : param->prm_d_mean_cycle = rand() / realrand_max * 120 + 90
  908. **  : param->prm_d_mean_phase = rand() / realrand_max * TWOPI
  909. **  : param->prm_d_range_cycle = rand() / realrand_max * 120 + 90
  910. **  : param->prm_d_range_phase = rand() / realrand_max * TWOPI
  911. **  ENDROUTINE
  912. */
  913. {
  914.     /* 
  915.     ** Returns a randomized parameter of any type 
  916.     ** Periods are 1.5 to 3.5 minutes
  917.     */
  918.     register double realrand_max;
  919.     
  920.     realrand_max = (double)RAND_MAX;
  921.     param->prm_d_mean_cycle 
  922.         = ((double)rand() / realrand_max) * 120.0 + 90.0;
  923.     param->prm_d_mean_phase 
  924.         = ((double)rand() / realrand_max) * TWOPI - PI;
  925.     param->prm_d_range_cycle 
  926.         = ((double)rand() / realrand_max) * 120.0 + 90.0;
  927.     param->prm_d_range_phase 
  928.         = ((double)rand() / realrand_max) * TWOPI - PI;
  929.     return;
  930. }
  931.  
  932. static void make_event(NOTE_EVENT_TYPE *new_event, CHARACTER_TYPE *style, 
  933.                       struct timeval *music_time, const int event_index,
  934.                       NOTE_LEN_TYPE *note_len)
  935. /*
  936. ** FUNCTIONAL DESCRIPTION:
  937. **  Calculate and play a note
  938. **
  939. ** ARGUMENTS:
  940. **
  941. **  new_event-
  942. **         description: One voice's event structure
  943. **           data_type: pointer to NOTE_EVENT_TYPE
  944. **              access: read/write
  945. **
  946. **  style-
  947. **         description: current ranges of pitch/dynamic/duration/texture
  948. **           data_type: pointer to CHARACTER_TYPE
  949. **              access: read only
  950. **
  951. **  music_time-
  952. **         description: relative time since starting playing music
  953. **           data_type: pointer to timeval
  954. **              access: read only
  955. **
  956. **  event_index-
  957. **         description: number of voice
  958. **           data_type: int
  959. **              access: read only
  960. **
  961. ** DESIGN
  962. /*
  963. ** ROUTINE make_event()
  964. **  : IF voice is playing (won't happen any more)
  965. **  : : turn off the note
  966. **  : : #if not CLI
  967. **  : : IF recording
  968. **  : : : record note off
  969. **  : : ENDIF
  970. **  : : #endif
  971. **  : ENDIF
  972. **  : IF this voice is higher than the current number of voices playing
  973. **  : : return
  974. **  : ENDIF
  975. **  : IF dynamic_range > 1
  976. **  : : new_dynamic = (rand() % style->ch_i_dynamic_range) 
  977. **            - (style->ch_i_dynamic_range / 2) + style->ch_i_dynamic_mean
  978. **  : ELSE
  979. **  : : new_dynamic = style->ch_i_dynamic_mean
  980. **  : ENDIF
  981. **  : check bounds on new_dynamic and fold in if necessary
  982. **  : new_event->nv_i_dynamic = new_dynamic
  983. **  : IF duration range is > 1 ms
  984. **  : : new_duration = (rand() % style->ch_i_dur_range 
  985. **                  - style->ch_i_dur_range / 2 + style->ch_i_dur_mean)
  986. **  : ELSE
  987. **  : : new_duration = style->ch_i_dur_mean
  988. **  : ENDIF
  989. **  : IF new_duration is shorter than min_note_len_ms 
  990. **  : : force new_duration = min_note_len_ms
  991. **  : ENDIF
  992. **  : IF new_duration is longer than max_note_len_ms
  993. **  : : force new_duration = max_note_len_ms
  994. **  : ENDIF
  995. **  : convert new_duration from millisecs to sys time in new_duration_time
  996. **  : copy new_duration_time to new_event->nr_r_duration
  997. **  : new_event->nv_r_stop_time = music_time + duration
  998. **  : IF voice is walking
  999. **  : : high_note = current pitch is >= highest pitch allowed in voice
  1000. **  : : low_note = current pitch is <= lowest pitch allowed in voice
  1001. **  : : walk = rand() % 3 - 1
  1002. **  : : IF low_note
  1003. **  : : : walk = 1
  1004. **  : : ENDIF
  1005. **  : : IF high_note
  1006. **  : : : walk = -1
  1007. **  : : ENDIF
  1008. **  : : new_pitch_index = new_event->nv_i_scale_index + walk
  1009. **  : ELSE
  1010. **  : : IF pitch_range > 0
  1011. **  : : : new_pitch_index = rand() % style->ch_i_pitch_range 
  1012. **        - style->ch_i_pitch_range / 2  + style->ch_i_pitch_mean
  1013. **  : : ELSE
  1014. **  : : : new_pitch_index = style->ch_i_pitch_mean
  1015. **  : : ENDIF
  1016. **  : : high_note = pitch > highest pitch allowed in voice
  1017. **  : : low_note = pitch < lowest pitch allowed in voice
  1018. **  : : ok_rand_note = not high_note or low_note
  1019. **  : ENDIF
  1020. **  : IF pitch index >= range
  1021. **  : : new_pitch_index = range -1 fold under if too high
  1022. **  : ENDIF
  1023. **  : IF new_pitch_index < 0
  1024. **  : : new_pitch_index = 0
  1025. **  : ENDIF
  1026. **  : new_event->nv_i_scale_index = new_pitch_index
  1027. **  : new_event->nv_i_scale_index = scale[new_pitch_index]
  1028. **  : IF (playing AND ok_rand_note)
  1029. **  : : play_note_on(new_event)
  1030. **  : : set playing for voice
  1031. **  : : #if not CLI
  1032. **  : : IF recording
  1033. **  : : : record_note_event(new_event)
  1034. **  : : ENDIF
  1035. **  : ENDIF
  1036. ** ENDROUTINE
  1037. */
  1038. {
  1039.     auto int    low_note,        /* Boolean flag that the note is as the */
  1040.                                 /* bottom of the scale                   */
  1041.                 high_note,      /* Boolean flag = note is at top of scale*/
  1042.                 ok_rand_note = TRUE, /* not low and not high             */
  1043.                 walk,           /* a direction for the note to walk      */
  1044.                 new_pitch_index,  /* index to the new pitch              */
  1045.                 new_dynamic,     /* temp new dynamic value               */
  1046.                 new_duration; /* a temporary new duration value          */
  1047.     auto struct timeval new_duration_time;
  1048.  
  1049.     if (new_event->nv_i_playing)
  1050.     {
  1051.         new_event->nv_i_dynamic = 0;
  1052.         new_event->nv_r_stop_time = *music_time;
  1053.         if (new_event->nv_i_was_audio)
  1054.         {
  1055.             play_audio_note(new_event);
  1056.         }
  1057.         else
  1058.         {
  1059.             play_note_on(new_event);    /* Turn off the old note */
  1060.         }
  1061.         new_event->nv_i_playing = FALSE;
  1062. #ifndef CLI
  1063.         if (recording)
  1064.         {
  1065.             record_note_event(new_event);
  1066.         }
  1067. #endif
  1068.     }
  1069.     if (event_index > style->ch_i_thickness)
  1070.     {
  1071.         return;
  1072.     }
  1073.     if (style->ch_i_dynamic_range > 1)
  1074.     {
  1075.         new_dynamic = (rand() % style->ch_i_dynamic_range) 
  1076.                     - (style->ch_i_dynamic_range / 2)
  1077.                     + style->ch_i_dynamic_mean;
  1078.     }
  1079.     else
  1080.     {
  1081.         new_dynamic = style->ch_i_dynamic_mean;
  1082.     }
  1083.     /*
  1084.     ** Check boundaries on dynamic
  1085.     */
  1086.     if (new_dynamic > 127)
  1087.     {
  1088.         new_dynamic = 127;
  1089.     }
  1090.     if (new_dynamic < 30 )
  1091.     {
  1092.         new_dynamic = 30;
  1093.     }
  1094.     new_event->nv_i_dynamic = new_dynamic; /* make dynamic a byte*/
  1095.  
  1096.     if (style->ch_i_dur_range > 1)
  1097.     {
  1098.         new_duration  = (rand() % style->ch_i_dur_range 
  1099.                         - style->ch_i_dur_range / 2
  1100.                         + style->ch_i_dur_mean);
  1101.     }
  1102.     else
  1103.     {
  1104.         new_duration = style->ch_i_dur_mean;
  1105.     }
  1106.     new_duration = 
  1107.       ((new_duration < note_len->len_i_min) 
  1108.       ? note_len->len_i_min : new_duration);
  1109.     new_duration = 
  1110.       ((new_duration > note_len->len_i_max) 
  1111.       ? note_len->len_i_max : new_duration);
  1112.     new_duration_time.tv_secs = new_duration / 1000;
  1113.     new_duration_time.tv_micro = (new_duration * 1000) % 1000000;
  1114.     /* 
  1115.     ** Put duration in durations list 
  1116.     */
  1117.     new_event->nv_r_duration = new_duration_time;
  1118.     new_event->nv_r_start_time = *music_time;
  1119.     new_event->nv_r_stop_time = new_duration_time;
  1120.     AddTime(&new_event->nv_r_stop_time, music_time);
  1121.  
  1122.     /* 
  1123.     ** If the voice is walking
  1124.     */
  1125.     if (new_event->nv_i_walking)
  1126.     {
  1127.         high_note 
  1128.             = new_event->nv_i_cur_pitch >= new_event->nv_i_high_pitch;
  1129.         low_note  = new_event->nv_i_cur_pitch <= new_event->nv_i_low_pitch;
  1130.         walk = (rand() % 3) - 1;
  1131.         if (low_note)
  1132.         {
  1133.             walk = 1;
  1134.         }
  1135.         if (high_note)
  1136.         {
  1137.             walk = -1;
  1138.         }
  1139.         new_pitch_index = new_event->nv_i_scale_index + walk;
  1140.     }
  1141.     else
  1142.     {
  1143.         if (style->ch_i_pitch_range > 0)
  1144.         {
  1145.             new_pitch_index = rand() % style->ch_i_pitch_range 
  1146.                             - style->ch_i_pitch_range / 2
  1147.                             + style->ch_i_pitch_mean;
  1148.         }
  1149.         else
  1150.         {
  1151.             new_pitch_index = style->ch_i_pitch_mean;
  1152.         }
  1153.         high_note = (scale[new_pitch_index] >= new_event->nv_i_high_pitch);
  1154.         low_note = (scale[new_pitch_index] <= new_event->nv_i_low_pitch);
  1155.         ok_rand_note = !(high_note || low_note);
  1156.     }
  1157.     if (new_pitch_index >= range)
  1158.     {
  1159.         new_pitch_index = range - 1;    /* fold under if too high   */
  1160.     }
  1161.     if (new_pitch_index < 0)
  1162.     {    
  1163.         new_pitch_index = 0;            /* fold up if too low       */
  1164.     }
  1165.     new_event->nv_i_scale_index = new_pitch_index;
  1166.     new_event->nv_i_cur_pitch = scale[new_pitch_index];
  1167.     /*
  1168.     ** Play the note
  1169.     */
  1170.     if (playing && ok_rand_note)
  1171.     {
  1172.         if (new_event->nv_i_audio)
  1173.         {
  1174.             play_audio_note(new_event);
  1175.         }
  1176.         else
  1177.         {
  1178.             play_note_on(new_event);
  1179.         }
  1180.         new_event->nv_i_playing = TRUE;
  1181. #ifndef CLI
  1182.         if (recording)
  1183.         {
  1184.             record_note_event(new_event);
  1185.         }
  1186. #endif
  1187.     }
  1188.     return;
  1189. }
  1190.  
  1191. #ifndef CLI
  1192. static void parse_menu(const int class, const int code,
  1193.                      struct timeval *str_time, FORM_TYPE *form,
  1194.                      struct timeval *duration, int *tempo, 
  1195.                      NOTE_EVENT_TYPE *events, NOTE_LEN_TYPE *note_len) 
  1196. /*
  1197. ** FUNCTIONAL DESCRIPTION:
  1198. **  Interpret Intuition mouse/menu operations
  1199. **
  1200. ** ARGUMENTS:
  1201. **
  1202. **  class-
  1203. **         description: Intuition event class
  1204. **           data_type: int
  1205. **              access: read only
  1206. **
  1207. **  code-
  1208. **         description: Intuition event code
  1209. **           data_type: int
  1210. **              access: read only
  1211. **
  1212. **  str_time-
  1213. **         description: the real clock time that the piece started playing
  1214. **           data_type: pointer to struct timeval
  1215. **              access: write only
  1216. **
  1217. **  form-
  1218. **         description: period/phase of mean/range for pitch/dyn/rhyth/text
  1219. **           data_type: pointer to FORM_TYPE
  1220. **              access: read/write
  1221. **
  1222. **  duration-
  1223. **         description: Length of piece
  1224. **           data_type: pointer to struct timeval
  1225. **              access: read/write
  1226. **
  1227. **  tempo-
  1228. **         description: beats per second
  1229. **           data_type: pointer to int
  1230. **              access: read/write
  1231. **
  1232. **  events-
  1233. **         description: 
  1234. **           data_type: pointer to NOTE_EVENT_TYPE 
  1235. **              access: read/write
  1236. **
  1237. ** DESIGN:
  1238. **
  1239. **  ROUTINE parse_menu
  1240. **  : init file_req
  1241. **  : CASE class
  1242. **  : : NEWSIZE
  1243. **  : : : draw_form()
  1244. **  : : MENUPICK
  1245. **  : : : IF code != MENUNULL
  1246. **  : : : : item = ITEMNUM(code)
  1247. **  : : : : subitem = SUBNUM(code)
  1248. **  : : : : CASE MENUNUM(code)
  1249. **  : : : : : PROJECT:
  1250. **  : : : : : : IF item != NOITEM
  1251. **  : : : : : : : CASE item
  1252. **  : : : : : : : : 0
  1253. **  : : : : : : : : : set gi_quit 
  1254. **  : : : : : : : : 1
  1255. **  : : : : : : : : : response = AutoRequest thanks
  1256. **  : : : : : : : : 2 
  1257. **  : : : : : : : : : IF AslBase != NULL
  1258. **  : : : : : : : : : : use file requester to get MIDI file name
  1259. **  : : : : : : : : 3
  1260. **  : : : : : : : : : erase_recording
  1261. **  : : : : : : : : 4
  1262. **  : : : : : : : : : record_init
  1263. **  : : : : : : : : : toggle recording
  1264. **  : : : : : : : : 5
  1265. **  : : : : : : : : : use file requester to save form file
  1266. **  : : : : : : : : 6
  1267. **  : : : : : : : : : use file requester to load form file
  1268. **  : : : : : : : : 7 
  1269. **  : : : : : : : : : continue playing
  1270. **  : : : : : : : : 8 
  1271. **  : : : : : : : : : stop playing
  1272. **  : : : : : : : : 9 
  1273. **  : : : : : : : : : Start playing music
  1274. **  : : : : : : : FORM
  1275. **  : : CLOSEWINDOW
  1276. **  : : : gi_quit = TRUE
  1277. **  ENDROUTINE
  1278. */
  1279. {
  1280.     auto int    asl_result,
  1281.                 vox_index,
  1282.                 sts,            /* file status */
  1283.                 item = 0,       /* menu item */
  1284.                 subitem = 0,    /* menu subitem */
  1285.                 response;
  1286.     static struct FileRequester *asl_request;
  1287.     static struct TagItem asl_tags[8] =
  1288.         {{ASL_Dir,     NULL}, {ASL_File,     NULL}, {ASL_Hail,     NULL},
  1289.         {ASL_Window,   NULL}, {ASL_LeftEdge, 10},   {ASL_TopEdge,  10},
  1290.         {ASL_Height,  180}, {TAG_DONE,     NULL}};
  1291. #ifdef MEASURE
  1292.     static unsigned char measure_string[32];
  1293.     static struct IntuiText measure_txt 
  1294.         = {2, 1, JAM2, 15, 10, &font_choice, measure_string, NULL};
  1295. #endif
  1296.     const struct timeval zero_time = {0, 0};
  1297.     static struct timeval sys_time;
  1298.     static struct ReqFileRequester file_req;
  1299.     static char midi_path[96] = "\0",
  1300.                 midi_dir[96]  = "\0",
  1301.                 midi_file[64] = "\0",
  1302.                 form_path[64] = "\0",
  1303.                 form_dir[96]  = "\0",
  1304.                 form_file[64] = "\0",
  1305.                 title_string[128]  = "\0";
  1306.     /* 
  1307.     ** Strings for gadgets 
  1308.     */
  1309.     static char load_file_banner[]    = "Load Form File",
  1310.                 save_form_banner[]    = "Save Form File",
  1311.                 midi_form_banner[]    = "Save MIDI file",
  1312.                 about1_str[32]        = "Welcome to AlgoRhythms 3.1",
  1313.                 about2_str[40]        = "Copyright 1994 Thomas E. Janzen",
  1314.                 thanks_str[7]         = "Thanks";
  1315.     static struct IntuiText 
  1316.         about1_txt = {2, 1, JAM2, 5, 4, &font_choice, about1_str, NULL},
  1317.         about2_txt = {2, 1, JAM2, 5, 15, NULL, about2_str, &about1_txt},
  1318.         thanks_txt = {2, 1, JAM1, 5, 4, NULL, thanks_str, NULL};
  1319.     
  1320.     file_req.dirnamescolor = 2;
  1321.     file_req.devicenamescolor = 2;
  1322.     
  1323.     switch (class) 
  1324.     {
  1325.         case NEWSIZE: /* Window has been re-sized, so re-draw the graph */
  1326.             draw_form(duration, form);
  1327.             break;
  1328.         case MENUPICK: /* a menu selection was made */
  1329.             if (code != MENUNULL)
  1330.             {
  1331.                 item = ITEMNUM(code);
  1332.                 subitem = SUBNUM(code);
  1333.                 switch (MENUNUM(code)) 
  1334.                 {
  1335.                     case PROJECT: /* Project menu strip was selected */
  1336.                     if (item != NOITEM) 
  1337.                     switch (item) 
  1338.                     {
  1339.                         case 9: /* gi_quit program */
  1340.                             gi_quit = TRUE;
  1341.                             break;
  1342.                         case 8: /* About copyright notice */
  1343.                             response 
  1344.                                 = AutoRequest(w, &about2_txt, &thanks_txt, 
  1345.                                            &thanks_txt, 0L, 0L, 300L, 60L);
  1346.                             break;
  1347.                         case 7: /* Save MIDI file */
  1348.                             asl_tags[0].ti_Data = (ULONG)midi_dir;
  1349.                             asl_tags[1].ti_Data = (ULONG)midi_file;
  1350.                             asl_tags[2].ti_Data 
  1351.                                 = (ULONG)midi_form_banner;
  1352.                             asl_tags[3].ti_Data = (ULONG)w;
  1353.                             asl_request 
  1354.                                 = AllocAslRequest(ASL_FileRequest,
  1355.                                                   asl_tags);
  1356.                             asl_result = AslRequest(asl_request, NULL);
  1357.                             if (NULL == asl_result)
  1358.                             {
  1359.                                 DisplayBeep(NULL);
  1360.                                 break;
  1361.                             }
  1362.                             strcpy(midi_dir, asl_request->rf_Dir);
  1363.                             strcpy(midi_file, asl_request->rf_File);
  1364.                             strcpy(midi_path, asl_request->rf_Dir);
  1365.                             if ( (midi_path[strlen(midi_path) - 1] != ':')
  1366.                               && (strlen(midi_path) != 0))
  1367.                             {
  1368.                                 strcat(midi_path, "/");
  1369.                             }
  1370.                             strcat(midi_path, asl_request->rf_File);
  1371.                             write_midi(midi_path);
  1372.                             FreeAslRequest(asl_request);
  1373.                             break;
  1374.                         case 6: /* Erase */
  1375.                             erase_recording();
  1376.                             break;
  1377.                         case 5: /* Record */
  1378.                             recording = record_init();
  1379.                             break;
  1380.                         case 4: /* Save a form file */
  1381.                             asl_tags[0].ti_Data = (ULONG)form_dir;
  1382.                             asl_tags[1].ti_Data = (ULONG)form_file;
  1383.                             asl_tags[2].ti_Data 
  1384.                                 = (ULONG)save_form_banner;
  1385.                             asl_tags[3].ti_Data = (ULONG)w;
  1386.                             asl_request 
  1387.                                 = AllocAslRequest(ASL_FileRequest,
  1388.                                     asl_tags);
  1389.                             asl_result = AslRequest(asl_request, NULL);
  1390.                             if (NULL == asl_result)
  1391.                             {
  1392.                                 DisplayBeep(NULL);
  1393.                                 break;
  1394.                             }
  1395.                             strcpy(form_dir, asl_request->rf_Dir);
  1396.                             strcpy(form_file, asl_request->rf_File);
  1397.                             strcpy(form_path, asl_request->rf_Dir);
  1398.                             if ((form_path[strlen(form_path) - 1] != ':')
  1399.                               && (strlen(form_path) != 0))
  1400.                             {
  1401.                                 strcat(form_path, "/");
  1402.                             }
  1403.                             strcat(form_path, asl_request->rf_File);
  1404.                             sts = save_file(form_path, duration, &range, 
  1405.                                             scale, &num_voices, tempo,
  1406.                                             form, events, note_len);
  1407.                             if (sts)
  1408.                             {
  1409.                                 DisplayBeep(NULL);
  1410.                                 break;
  1411.                             }
  1412.                             strcpy(title_string, form_path);
  1413.                             SetWindowTitles(w, title_string, (void *) -1L);
  1414.                             FreeAslRequest(asl_request);
  1415.                             break;
  1416.                         case 3: /* load a form file */
  1417.                             asl_tags[0].ti_Data = (ULONG)form_dir;
  1418.                             asl_tags[1].ti_Data = (ULONG)form_file;
  1419.                             asl_tags[2].ti_Data 
  1420.                                 = (ULONG)load_file_banner;
  1421.                             asl_tags[3].ti_Data = (ULONG)w;
  1422.                             asl_request 
  1423.                                 = AllocAslRequest(ASL_FileRequest,
  1424.                                                   asl_tags);
  1425.                             asl_result = AslRequest(asl_request, NULL);
  1426.                             if (NULL == asl_result)
  1427.                             {
  1428.                                 DisplayBeep(NULL);
  1429.                                 break;
  1430.                             }
  1431.                             strcpy(form_dir, asl_request->rf_Dir);
  1432.                             strcpy(form_file, asl_request->rf_File);
  1433.                             strcpy(form_path, asl_request->rf_Dir);
  1434.                             if ((form_path[strlen(form_path)- 1] != ':')
  1435.                               && (strlen(form_path) != 0))
  1436.                             {
  1437.                                 strcat(form_path, "/");
  1438.                             }
  1439.                             strcat(form_path, asl_request->rf_File);
  1440.                             sts = read_file(form_path,duration, &range, 
  1441.                                     scale, &num_voices, tempo,
  1442.                                     form, events, note_len);
  1443.                             if (!sts) 
  1444.                             {
  1445.                                 if (0 == *tempo) 
  1446.                                 {
  1447.                                     delay_ticks = 0;
  1448.                                 }
  1449.                                 else 
  1450.                                 {
  1451.                                     delay_ticks = 50 / (*tempo);
  1452.                                 }
  1453.                                 half_range = range / 2;
  1454.                                 draw_form(duration, form);
  1455.                                 strcpy(title_string, form_path);
  1456.                                 SetWindowTitles(w, title_string, 
  1457.                                     (void *) -1L);
  1458.                                 range_time = zero_time;
  1459.                                 set_form_gadgets(form, duration, 
  1460.                                 tempo, events, note_len, num_voices);
  1461.                                 set_voice_gadgets(events);
  1462.                             }
  1463.                             else
  1464.                             {
  1465.                                 DisplayBeep(NULL);
  1466.                             }
  1467.                             FreeAslRequest(asl_request);
  1468.                             break;
  1469.                         case 2: /* Continue after stopping */
  1470.                             started = TRUE;
  1471.                             play_note_on(&reset_event);
  1472.                             GetSysTime(&sys_time);
  1473.                             str_time->tv_secs = str_time->tv_secs 
  1474.                                 + (sys_time.tv_secs - stop_time.tv_secs);
  1475.                             break;
  1476.                         case 1: /* stop the music but don't exit */
  1477.                             started = FALSE;
  1478.                             for (vox_index = 0; 
  1479.                                  vox_index < MAXVOICE; vox_index++)
  1480.                             {
  1481.                                 events[vox_index].nv_r_start_time 
  1482.                                 = events[vox_index].nv_r_duration 
  1483.                                 = events[vox_index].nv_r_stop_time 
  1484.                                 = zero_time;
  1485.                             }
  1486.                             stop_all_notes(events);
  1487.                             GetSysTime(&stop_time);
  1488. #ifdef MEASURE
  1489.                             sprintf(measure_string,"%d", gi_notes_measure);
  1490.                             PrintIText(rast_port, &measure_txt, 1, 1);
  1491.                             gi_notes_measure = 0;
  1492. #endif
  1493.                             break;
  1494.                         case 0: /* Start to Play music */
  1495.                             started = TRUE;
  1496.                             done = FALSE;
  1497.                             GetSysTime(str_time);
  1498.                             play_note_on(&reset_event);
  1499.                             for (vox_index = 0; 
  1500.                                  vox_index < MAXVOICE; vox_index++)
  1501.                             {
  1502.                                 events[vox_index].nv_r_start_time
  1503.                                 = events[vox_index].nv_r_duration
  1504.                                 = events[vox_index].nv_r_stop_time
  1505.                                 = zero_time;
  1506.                             }
  1507.                             send_function(STARTFUNCT);
  1508.                             range_time = zero_time;
  1509.                             break;
  1510.                         default:
  1511.                             break;
  1512.                     } /*switch itemnum*/
  1513.                     break;
  1514.                     case FORM: /* Form menu strip was selected */
  1515.                         if (item != NOITEM) 
  1516.                         switch (item) 
  1517.                         {
  1518.                             case 0: /* ReDraw */
  1519.                                 draw_form(duration, form);
  1520.                                 break;
  1521.                             case 1: /* Form */
  1522.                                 open_forms_window();
  1523.                                 break;
  1524.                             case 2: /* Voice */
  1525.                                 open_voices_window();
  1526.                                 break;
  1527.                             case 3: /* Orchestra */
  1528.                                 open_orch_window();
  1529.                                 break;
  1530.                             case 4: /* Colors */
  1531.                                 open_colors_window();
  1532.                                 break;
  1533.                             default:
  1534.                                 break;
  1535.                         } /* switch itemnum */
  1536.                         break;
  1537.                 } /* switch menunum */
  1538.             } /* while not menunull */
  1539.             break;
  1540.             case CLOSEWINDOW:
  1541.                 gi_quit = TRUE;
  1542.                 break;
  1543.             default:
  1544.                 break;
  1545.     } /* switch class */
  1546.     return;
  1547. } /* end function */
  1548. #endif
  1549.  
  1550. static double cvt_time_secs(struct timeval *Time)
  1551. /*
  1552. ** FUNCTIONAL DESCRIPTION:
  1553. **  Converts system time into floating seconds
  1554. **
  1555. ** RETURN VALUE:
  1556. **      description: floating point seconds
  1557. **        data_type: double
  1558. **
  1559. ** ARGUMENTS:
  1560. **
  1561. **  Time-
  1562. **         description: time
  1563. **           data_type: pointer to struct timeval
  1564. **              access: read only
  1565. **
  1566. ** DESIGN:
  1567. **
  1568. ** ROUTINE cvt_time_secs
  1569. **  : return Time->tv_secs + Time->tv_micro / 1E6
  1570. ** ENDROUTINE
  1571. */
  1572. {
  1573.     return (double)Time->tv_secs + ((double)Time->tv_micro / 1E6);
  1574. }
  1575.  
  1576. static void stop_note(  NOTE_EVENT_TYPE *new_event, 
  1577.                         struct timeval *music_time)
  1578. /*
  1579. ** FUNCTIONAL DESCRIPTION:
  1580. **  Turn off a note
  1581. **
  1582. ** ARGUMENTS:
  1583. **
  1584. **  new_event-
  1585. **         description: a voice
  1586. **           data_type: pointer to NOTE_EVENT_TYPE
  1587. **              access: read/write
  1588. **
  1589. **  music_time-
  1590. **         description: Current relative time since starting to play
  1591. **           data_type: pointer to struct timeval
  1592. **              access: read only
  1593. **
  1594. ** DESIGN
  1595. **
  1596. ** ROUTINE stop_note
  1597. **  : IF note is playing
  1598. **  : : clear dynamic
  1599. **  : : set stop time to music time
  1600. **  : : play_note_on(new_event)
  1601. **  : : clear playing flag
  1602. ** #if not CLI
  1603. **  : : IF recording
  1604. **  : : : record_note_event(new_event)
  1605. **  : : ENDIF
  1606. **  : ENDIF
  1607. ** ENDROUTINE
  1608. */
  1609. {
  1610.     if (new_event->nv_i_playing)
  1611.     {
  1612.         new_event->nv_i_dynamic = 0;
  1613.         new_event->nv_r_stop_time = *music_time;
  1614.         if (new_event->nv_i_was_audio)
  1615.         {
  1616.             play_audio_note(new_event);
  1617.         }
  1618.         else
  1619.         {
  1620.             play_note_on(new_event);   /* Turn off the old note */
  1621.         }
  1622.         new_event->nv_i_playing = FALSE;
  1623. #ifndef CLI
  1624.         if (recording)
  1625.         {
  1626.             record_note_event(new_event);
  1627.         }
  1628. #endif
  1629.     }
  1630.     return;
  1631. }
  1632.